﻿// Description: Classes for reading ESRI shapefiles.
// Reference:   ESRI Shapefile Technical Description, July 1998.
//              http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf
// 2007-01-22 nschan Initial revision.

using System;
using System.Data;
using System.Data.OleDb;
using System.IO;
using System.Text;

namespace SSISComponents
{
    /// <summary>
    /// The ShapeFileReadInfo class stores information about a shapefile
    /// that can be used by external clients during a shapefile read.
    /// </summary>
    public class ShapeFileReadInfo
    {
        #region Private fields
        private string fileName;
        private ShapeFile shapeFile;
        private Stream stream;
        private int numBytesRead;
        private int recordIndex;
        #endregion Private fields

        #region Constructor
        /// <summary>
        /// Constructor for the ShapeFileReadInfo class.
        /// </summary>
        public ShapeFileReadInfo()
        {
        }
        #endregion Constructor

        #region Properties
        /// <summary>
        /// The full pathname of the shapefile.
        /// </summary>
        public string FileName
        {
            get { return this.fileName; }
            set { this.fileName = value; }
        }

        public string dbaseFile
        {
            get { return this.fileName.Replace(".shp", ".dbf"); }
        }

        /// <summary>
        /// A reference to the shapefile instance.
        /// </summary>
        public ShapeFile ShapeFile
        {
            get { return this.shapeFile; }
            set { this.shapeFile = value; }
        }

        /// <summary>
        /// An opened file stream for a shapefile.
        /// </summary>
        public Stream Stream
        {
            get { return this.stream; }
            set { this.stream = value; }
        }

        /// <summary>
        /// The number of bytes read from a shapefile so far.
        /// Can be used to compute a progress value.
        /// </summary>
        public int NumberOfBytesRead
        {
            get { return this.numBytesRead; }
            set { this.numBytesRead = value; }
        }

        /// <summary>
        /// A general-purpose record index.
        /// </summary>
        public int RecordIndex
        {
            get { return this.recordIndex; }
            set { this.recordIndex = value; }
        }

        #endregion Properties

        #region Public methods
        /// <summary>
        /// Output some of the field values in the form of a string.
        /// </summary>
        /// <returns>A string representation of the field values.</returns>
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("ShapeFileReadInfo: FileName={0}, ", this.fileName);
            sb.AppendFormat("NumberOfBytesRead={0}, RecordIndex={1}", this.numBytesRead, this.recordIndex);

            return sb.ToString();
        }

        public static string ConnectionString(string FileName)
        {
            FileInfo fi = new FileInfo(FileName);
            string directory = fi.DirectoryName;
            return "PROVIDER=Microsoft.Jet.OLEDB.4.0;Data Source=" + directory + ";Extended Properties=dBASE 5.0";
        }

        public static string FilenameWithoutExt(string FileName)
        {
            // Get the filename minus the extension.
            FileInfo fi = new FileInfo(FileName);
            string fileNameNoExt = fi.Name.ToUpper(System.Globalization.CultureInfo.InvariantCulture);
            if (fileNameNoExt.EndsWith(".DBF"))
                fileNameNoExt = fileNameNoExt.Substring(0, fileNameNoExt.Length - 4);

            // Convert to a short filename (may not work in every case!).
            if (fileNameNoExt.Length > 8)
            {
                if (fileNameNoExt.Contains(" "))
                {
                    string noSpaces = fileNameNoExt.Replace(" ", "");
                    if (noSpaces.Length > 8)
                        fileNameNoExt = noSpaces;
                }
                fileNameNoExt = fileNameNoExt.Substring(0, 6) + "~1";
            }
            return fileNameNoExt;
        }

        public DataTable AttributeColumns()
        {
            DataTable dt = new DataTable();
            DataTable dtColumns = new DataTable("Columns");

            if (string.IsNullOrEmpty(dbaseFile))
                throw new ArgumentNullException("dbaseFile");

            // Check if the file exists. If it doesn't exist,
            // this is not an error.
            if (!File.Exists(dbaseFile))
                return dt;

            // Set the connection string.
            string connectionString = ShapeFileReadInfo.ConnectionString(dbaseFile);

            // Set the select query.
            string selectQuery = "SELECT TOP 1 * FROM [" + ShapeFileReadInfo.FilenameWithoutExt(dbaseFile) + "#DBF];";

            // Create a database connection object using the connection string.
            OleDbConnection connection = new OleDbConnection(connectionString);

            // Create a database command on the connection using the select query.
            OleDbCommand command = new OleDbCommand(selectQuery, connection);

            try
            {
                // Open the connection.          
                connection.Open();

                // Create a data adapter to fill a dataset.
                OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
                dataAdapter.SelectCommand = command;
                dt.Locale = System.Globalization.CultureInfo.InvariantCulture;
                dataAdapter.Fill(dt);

                DataColumn dc = new DataColumn("Name", System.Type.GetType("System.String"));
                dtColumns.Columns.Add(dc);
                foreach (DataColumn col in dt.Columns)
                {
                    DataRow dr = dtColumns.NewRow();
                    dr[0] = col.ColumnName;
                    dtColumns.Rows.Add(dr);
                }

            }
            catch (OleDbException)
            {
                // Note: An exception will occur if the filename of the dBASE
                // file does not follow 8.3 naming conventions. In this case,
                // you must use its short (MS-DOS) filename.

                // Rethrow the exception.
                throw;
            }
            finally
            {
                // Dispose of connection.
                ((IDisposable)connection).Dispose();
            }
            return dtColumns;
        }

        #endregion Public methods
    }
}
